package com.open4cn.simulator.device;

import com.open4cn.simulator.connect.com.SerialPortConnector;
import com.open4cn.simulator.connect.com.SerialPortQuickConnector;
import com.open4cn.simulator.connect.tcp.TCPClientConnector;
import com.open4cn.simulator.connect.tcp.TCPClientQuickConnector;
import com.open4cn.simulator.connect.tcp.TCPServerConnector;
import com.open4cn.simulator.connect.tcp.TCPServerQuickConnector;
import com.open4cn.simulator.core.connector.ConnectAddress;
import com.open4cn.simulator.core.connector.ConnectorModeEnum;
import com.open4cn.simulator.core.connector.IConnector;
import com.open4cn.simulator.core.connector.serialport.SerialPortInboundHandler;
import com.open4cn.simulator.core.connector.serialport.SerialPortOutboundHandler;
import com.open4cn.simulator.core.device.AbstractDevice;
import com.open4cn.simulator.core.device.DeviceInfo;
import com.open4cn.simulator.core.device.DeviceParameters;
import com.open4cn.simulator.core.device.DeviceStatus;
import com.open4cn.simulator.core.device.IDevice;
import com.open4cn.simulator.core.exception.IllegalParameterException;
import com.open4cn.simulator.core.exception.UnableToProcessException;
import com.open4cn.simulator.core.parser.IInstructionParser;
import com.open4cn.simulator.core.parser.InstructionParserChain;
import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelOutboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author yinxiaopeng
 * @version 1.0.0
 * @company 致景科技
 * @department 飞梭BU/研发
 * @description
 * @Email yinxiaopeng@zj.tech
 * @date 2022-07-24 20:50
 */
public class DeviceQuickBuildHelper {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private DeviceParameters deviceParameters;

    // 可为空
    private Class<? extends IDevice> deviceClass;

    // 缺省默认new Object
    private Object customDeviceParameters = new Object();

    // 缺省默认Object.class
    private Class customDeviceStatusClass = Object.class;

    private ConnectorModeEnum connectorMode;

    private Class<? extends ChannelInboundHandler> decoderClass;

    private SerialPortInboundHandler decoder;

    private Class<? extends ChannelOutboundHandler> encoderClass;

    private SerialPortOutboundHandler encoder;

    private List<Class<? extends IInstructionParser>> instructionParserClassList;

    private DeviceQuickBuildHelper() {
    }

    public static DeviceQuickBuildHelper start() {
        return new DeviceQuickBuildHelper();
    }


    public DeviceQuickBuildHelper device(Class<? extends IDevice> deviceClass) {
        this.deviceClass = deviceClass;
        return this;
    }

    public DeviceQuickBuildHelper customDeviceParameters(Object customDeviceParameters) {
        this.customDeviceParameters = customDeviceParameters;
        return this;
    }

    public DeviceQuickBuildHelper customDeviceStatus(Class customDeviceStatusClass) {
        this.customDeviceStatusClass = customDeviceStatusClass;
        return this;
    }

    public DeviceQuickBuildHelper connectorMode(ConnectorModeEnum connectorMode) {
        this.connectorMode = connectorMode;
        return this;
    }

    public DeviceQuickBuildHelper deviceParameters(DeviceParameters deviceParameters) {
        this.deviceParameters = deviceParameters;
        return this;
    }

    public DeviceQuickBuildHelper decoder(Class<? extends ChannelInboundHandler> decoderClass) {
        this.decoderClass = decoderClass;
        return this;
    }

    public DeviceQuickBuildHelper decoder(SerialPortInboundHandler decoder) {
        this.decoder = decoder;
        return this;
    }

    public DeviceQuickBuildHelper encoder(Class<? extends ChannelOutboundHandler> encoderClass) {
        this.encoderClass = encoderClass;
        return this;
    }

    public DeviceQuickBuildHelper encoder(SerialPortOutboundHandler encoder) {
        this.encoder = encoder;
        return this;
    }

    public DeviceQuickBuildHelper instructionParser(Class<? extends IInstructionParser> instructionParserClass) {
        if (null == instructionParserClassList) {
            instructionParserClassList = new ArrayList<>();
        }
        if (!instructionParserClassList.contains(instructionParserClass)) {
            instructionParserClassList.add(instructionParserClass);
        }
        return this;
    }

    /**
     * 执行
     *
     * @return
     */
    public IDevice toBuild() {
        checkWhetherItCanBeBuild();
        // 创建设备
        AbstractDevice device = doDevice();

        // 创建设备info
        device.setDeviceInfo(doDeviceInfo());

        // 创建通信器
        device.setConnector(doConnector(device.getDeviceInfo()));

        return device;
    }


    private AbstractDevice doDevice() {
        if (null != deviceClass) {
            try {
                IDevice newInstance = deviceClass.newInstance();
                return (AbstractDevice) newInstance;
            } catch (Exception e) {
                logger.error("创建IDevice对象异常", e);
                throw new UnableToProcessException("创建IDevice对象异常");
            }
        }
        return new AbstractDevice() {
        };
    }


    private DeviceInfo doDeviceInfo() {
        DeviceInfo deviceInfo = new DeviceInfo<>();
        deviceInfo.setId(deviceParameters.getId());
        deviceInfo.setName(deviceParameters.getName());
        deviceInfo.setBrand(deviceParameters.getBrand());
        deviceInfo.setModel(deviceParameters.getModel());
        deviceInfo.setParameters(customDeviceParameters);
        deviceInfo.setDeviceStatus(doDeviceStatus());
        deviceInfo.setConnectAddress(doConnectAddress());
        return deviceInfo;
    }


    private DeviceStatus doDeviceStatus() {
        String deviceId = deviceParameters.getId();
        String brand = deviceParameters.getBrand();
        String model = deviceParameters.getModel();
        DeviceStatusManager deviceStatusManager = DeviceStatusManager.getInstance();
        DeviceStatus deviceStatus = deviceStatusManager.load(deviceId);
        if (null == deviceStatus) {
            deviceStatus = new DeviceStatus(deviceId, brand, model, doCustomDeviceStatus());
        }
        deviceStatusManager.put(deviceId, deviceStatus);
        return deviceStatus;
    }


    private Object doCustomDeviceStatus() {
        Object customDeviceStatus = null;
        try {
            customDeviceStatus = customDeviceStatusClass.newInstance();
        } catch (Exception e) {
            throw new UnableToProcessException("无法创建ConnectAddress对象");
        }
        return customDeviceStatus;
    }

    private ConnectAddress doConnectAddress() {
        if (ConnectorModeEnum.TCP_CLIENT.equals(connectorMode)) {
            return new ConnectAddress(deviceParameters.getString("host"), deviceParameters.getInteger("port"));
        }
        if (ConnectorModeEnum.TCP_SERVER.equals(connectorMode)) {
            return new ConnectAddress(deviceParameters.getInteger("port"));
        }
        if (ConnectorModeEnum.SERIAL_PORT.equals(connectorMode)) {
            return new ConnectAddress(deviceParameters.getInteger("baudRate"), deviceParameters.getString("comPort"));
        }
        throw new UnableToProcessException("无法创建ConnectAddress对象");
    }


    private IConnector doConnector(DeviceInfo deviceInfo) {
        DeviceStatus deviceStatus = deviceInfo.getDeviceStatus();
        InstructionParserChain chain = doChain(deviceInfo);

        // tcp client
        if (ConnectorModeEnum.TCP_CLIENT.equals(connectorMode)) {
            TCPClientConnector connector = new TCPClientQuickConnector(deviceStatus, chain);
            connector.decoder(decoderClass);
            connector.encoder(encoderClass);
            return connector;
        }


        // tcp server
        if (ConnectorModeEnum.TCP_SERVER.equals(connectorMode)) {
            TCPServerConnector connector = new TCPServerQuickConnector(deviceStatus, chain);
            connector.decoder(decoderClass);
            connector.encoder(encoderClass);
            return connector;
        }


        // 串口
        if (ConnectorModeEnum.SERIAL_PORT.equals(connectorMode)) {
            SerialPortConnector connector = new SerialPortQuickConnector(deviceStatus, chain);
            connector.decoder(decoder);
            connector.encoder(encoder);
            return connector;
        }
        throw new UnableToProcessException("无法创建IConnector对象");
    }


    private InstructionParserChain doChain(DeviceInfo deviceInfo) {
        Object customDeviceStatus = deviceInfo.getDeviceStatus().getData();
        List<IInstructionParser> parsers = instructionParserClassList.stream()
                .map(instructionParserClass -> doInstructionParser(instructionParserClass, customDeviceStatus))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        manageAnswer(deviceInfo.getId(), parsers);
        return new InstructionParserChain(parsers);
    }


    private IInstructionParser doInstructionParser(Class<? extends IInstructionParser> instructionParserClass, Object customDeviceStatus) {
        boolean match = false;
        Constructor constructorWithParams = null;
        Constructor<?>[] constructors = instructionParserClass.getConstructors();
        for (Constructor<?> constructor : constructors) {
            Class<?>[] parameterTypes = constructor.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                // 有参构造
                if (parameterType.equals(customDeviceStatusClass)) {
                    constructorWithParams = constructor;
                    match = true;
                    break;
                }
            }
            if (match) {
                break;
            }
        }

        IInstructionParser instructionParser = null;
        if (match) {
            try {
                instructionParser = (IInstructionParser) constructorWithParams.newInstance(customDeviceStatus);
            } catch (Exception e) {
                logger.error("有参构造创建IInstructionParser对象异常", e);
                throw new UnableToProcessException("创建IInstructionParser对象异常");
            }
        } else {
            try {
                instructionParser = instructionParserClass.newInstance();
            } catch (Exception e) {
                logger.error("无参构造创建IInstructionParser对象异常", e);
                throw new UnableToProcessException("创建IInstructionParser对象异常");
            }
        }
        return instructionParser;
    }

    private void manageAnswer(String deviceId, List<IInstructionParser> parsers) {
        AnswerManager answerManager = AnswerManager.getInstance();
        parsers.forEach(parser -> {
            answerManager.put(deviceId, parser.getAnswers());
        });
    }

    /**
     * 检查是否可以构建
     *
     * @return
     */
    private boolean checkWhetherItCanBeBuild() {
        if (null == deviceParameters) {
            throw new IllegalParameterException("缺少设备参数");
        }

        if (null == connectorMode) {
            throw new IllegalParameterException("请设置通信模式");
        }


        if ((ConnectorModeEnum.TCP_SERVER.equals(connectorMode) || ConnectorModeEnum.TCP_CLIENT.equals(connectorMode))
                && null == decoderClass) {
            throw new IllegalParameterException("请设置解码器");
        }

        if ((ConnectorModeEnum.TCP_SERVER.equals(connectorMode) || ConnectorModeEnum.TCP_CLIENT.equals(connectorMode))
                && null == encoderClass) {
            throw new IllegalParameterException("请设置编码器");
        }


        if (ConnectorModeEnum.SERIAL_PORT.equals(connectorMode) && null == decoder) {
            throw new IllegalParameterException("请设置解码器");
        }

        if (ConnectorModeEnum.SERIAL_PORT.equals(connectorMode) && null == encoder) {
            throw new IllegalParameterException("请设置编码器");
        }

        if (null == instructionParserClassList || instructionParserClassList.isEmpty()) {
            throw new IllegalParameterException("请指令解析器");
        }

        return true;
    }
}
